#include "PCResource.h"
#include <unistd.h>
#include <fstream>

#include "cJSON.h"

#define CONFIG_DIR "configs/"
#define CONFIG_FILE "pc_resource.json"

// 数据解析表 - 前导字符串
static const char *rsDataHeader[] = {
    "CPU usage",
    "CPU temp",
    "CPU freq",
    "CPU power",
    "GPU usage",
    "GPU temp",
    "GPU freq",
    "GPU power",
    "RAM usage",
    "RAM use",
    "NET upload speed",
    "NET download speed",
    "GRAM use",
    "GRAM usage",
    "Disk temp",
    "Disk usage",
    "CPU fan speed",
    "GPU fan speed",
    "Time",
    ":",
    "Date",
    "/",
    "/",
    "CPUM",
    "GPUM"};
// 数据解析表 - 单位
static const char *rsDataUnit[] = {
    "%",
    "C",
    "MHz",
    "W",
    "%",
    "C",
    "MHz",
    "W",
    "%",
    "MB",
    "KB/s",
    "KB/s",
    "MB",
    "%",
    "C",
    "%",
    "RPM",
    "RPM",
    ":",
    "{|}",
    "/",
    "/",
    "{|}",
    "Mod",
    "Mod"};

/**
 *@brief PCResource构造函数
 *@param ipaddr PC端内网IP地址
 *@param port  端口号
 *@param exitCb 退出进程回调函数
 */
PCResource::PCResource(function<void(void)> exitCb, mutex &UIMutex)
{
    threadExitFlag = false;

    PCResourceUI::Operations uiOpts;
    uiOpts.exitCb = exitCb;

    uiMutex = &UIMutex;

    if(getConfig() != true)
    {
        printf("can not read config from %s\n", CONFIG_DIR CONFIG_FILE);
        exitCb();
    }

    serverAddr.sin_family = AF_INET;                          // IPv4协议
    serverAddr.sin_port = htons(port);                        // 端口号
    inet_pton(AF_INET, ipAddr.c_str(), &serverAddr.sin_addr); // IP地址

    PCResourceUI::create(uiOpts);

    pThread = new std::thread(&PCResource::threadFunction, this); // 创建执行线程
}

PCResource::~PCResource()
{
    if (socketFd > 0)
        close(socketFd);
    threadExitFlag = true;
    pThread->join();
    delete pThread;

    PCResourceUI::release();
}

/**
 * @brief 获取配置参数
 */
bool PCResource::getConfig(void)
{
    ifstream file;

    file.open(CONFIG_DIR CONFIG_FILE, ios::in);

    if (file.is_open() != true)
        return false;

    char *buf = new char[4096];
    memset(buf, 0, 4096);
    file.read(buf, 4096);
    file.close();

    cJSON *cjson = cJSON_Parse(buf); // 打包cJSON数据格式
    if (cjson != nullptr)
    {
        cJSON *item = cJSON_GetObjectItem(cjson, "host_ip");
        if(item != nullptr)
            ipAddr = item->valuestring;
        
        item = cJSON_GetObjectItem(cjson, "port");
        if(item != nullptr)
            port = item->valueint;

        item = cJSON_GetObjectItem(cjson, "update_time");
        if(item != nullptr)
            updateTime = item->valueint;
    }

    cJSON_Delete(cjson);

    return true;
}

/**
 *@brief 获取PC硬件信息(由AIDA64发送)
 *@param info 保存信息的字符串(引用)
 *@return  获取成功返回0,失败返回-1
 */
int PCResource::getData(string &info)
{
    int ret = -1;

    do
    {
        socketFd = socket(AF_INET, SOCK_STREAM, 0); // 打开socket
        if (socketFd < 0)
        {
            perror("socket open error");
            break;
        }
        ret = connect(socketFd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); // 连接服务器
        if (ret < 0)
        {
            perror("socket connect error");
            break;
        }

        string getUrl = "GET /sse HTTP/1.1\r\nContent-Type=application/json;charset=utf-8\r\n\
                Host: " +
                        ipAddr + "\r\nUser-Agent=Mnipad\r\nConnection: close\r\n\r\n";

        ret = send(socketFd, getUrl.c_str(), getUrl.length(), 0); // 发送GET请求
        if (ret < 0)
        {
            perror("socket send error");
            break;
        }

        usleep(50000); // 线程休眠等待服务器反应
        char *buf = (char *)malloc(4096);

        ret = recv(socketFd, buf, 4096, 0); // 接收数据
        if (ret < 0)
        {
            perror("socket recv error");
            break;
        }

        info = string(buf); // 保存数据
        free(buf);
        ret = 0;
    } while (0);

    if (socketFd >= 0)
    {
        close(socketFd);
        socketFd = -1;
    }

    return ret;
}

/***
 *@brief 解析信息数据
 *@param info 保存信息的字符串(引用)
 *@return  PC硬件资源信息
 */
PCResInfo PCResource::analyticData(string &info)
{
    size_t dataStart = 0;
    size_t dataEnd = 0;
    string dataStr;
    PCResInfo resData;
    int data[23] = {0};
    int i;

    // 解析数据
    for (i = 0; i < 23; i++)
    {
        dataStart = info.find(rsDataHeader[i], dataEnd) + strlen(rsDataHeader[i]); // 寻找前导字符串
        dataEnd = info.find(rsDataUnit[i], dataStart);                             // 寻找单位字符串
        if (dataEnd != string::npos)
        {
            dataStr = info.substr(dataStart, dataEnd - dataStart);

            data[i] = atof(dataStr.c_str()) * 10; // 得到扩大10倍的整型数据
        }
    }

    dataStart = info.find(rsDataHeader[i]) + strlen(rsDataHeader[i]); // 寻找前导字符串
    dataEnd = info.find(rsDataUnit[i], dataStart);                    // 寻找单位字符串
    if (dataEnd != string::npos)
    {
        dataStr = info.substr(dataStart, dataEnd - dataStart);
        strcpy(resData.cpu_name, dataStr.c_str());
    }

    dataStart = info.find(rsDataHeader[i + 1]) + strlen(rsDataHeader[i + 1]); // 寻找前导字符串
    dataEnd = info.find(rsDataUnit[i + 1], dataStart);                        // 寻找单位字符串
    if (dataEnd != string::npos)
    {
        dataStr = info.substr(dataStart, dataEnd - dataStart);
        strcpy(resData.gpu_name, dataStr.c_str());
    }

    // 装载数据
    resData.cpu_usage = data[0] / 10; // CPU利用率
    resData.cpu_temp = data[1] / 10;  // CPU温度
    resData.cpu_freq = data[2] / 10;  // CPU主频
    resData.cpu_power = data[3];      // CPU功耗(扩大10倍)

    resData.gpu_usage = data[4] / 10; // GPU利用率
    resData.gpu_temp = data[5] / 10;  // GPU温度
    resData.gpu_freq = data[6] / 10;  // GPU核心频率
    resData.gpu_power = data[7];      // GPU功耗(扩大10倍)
    resData.ram_usage = data[8] / 10; // RAM使用率
    resData.ram_used = data[9] / 10;  // RAM使用量

    resData.net_upload_speed = data[10];   // net上行速率
    resData.net_download_speed = data[11]; // net下行速率

    resData.gpu_ram_used = data[12] / 10;  // GPU内存使用量
    resData.gpu_ram_usage = data[13] / 10; // GPU内存使用率
    resData.disk_temp = data[14] / 10;     // 硬盘温度
    resData.disk_usage = data[15] / 10;    // 硬盘使用率
    resData.cpu_fan_speed = data[16] / 10; // CPU风扇转速
    resData.gpu_fan_speed = data[17] / 10; // GPU风扇转速

    resData.hour = data[18] / 10;  // 时
    resData.min = data[19] / 10;   // 分
    resData.year = data[20] / 10;  // 年
    resData.month = data[21] / 10; // 月
    resData.date = data[22] / 10;  // 日

    return resData;
}

int PCResource::threadFunction(void)
{
    usleep(50000);
    string info = "null";
    PCResInfo resInfo;

    while (!threadExitFlag)
    {
        int ret = getData(info);
        if (ret == 0)
        {
            resInfo = analyticData(info);

            uiMutex->lock();
            PCResourceUI::update(resInfo);
            uiMutex->unlock();
        }

        usleep(updateTime * 1000);
    }

    return 0;
}
